package com.polito.mad.bikeponyapp.fragments;

import java.util.List;

import org.json.JSONException;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.database.Cursor;
import android.location.Location;
import android.os.AsyncTask;
import android.os.AsyncTask.Status;
import android.os.Bundle;
import android.support.v4.app.ListFragment;
import android.support.v4.app.LoaderManager;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.Loader;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.CompoundButton;
import android.widget.ImageButton;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;

import com.mobeta.android.dslv.DragSortListView;
import com.mobeta.android.dslv.DragSortListView.DragListener;
import com.mobeta.android.dslv.DragSortListView.DropListener;
import com.polito.mad.bikeponyapp.R;
import com.polito.mad.bikeponyapp.activity.MainActivity.ServiceStateChangeObserver;
import com.polito.mad.bikeponyapp.list.DeliveryListCursorLoader;
import com.polito.mad.bikeponyapp.list.ListViewAdapter;
import com.polito.mad.bikeponyapp.list.ListViewAdapter.OnShowSingleLocationListener;
import com.polito.mad.bikeponyapp.location.LocationObserver;
import com.polito.mad.bikeponyapp.persistence.DeliveriesRepository;
import com.polito.mad.bikeponyapp.persistence.DeliveryItem;
import com.polito.mad.bikeponyapp.persistence.ModelObserver;
import com.polito.mad.bikeponyapp.persistence.RepositoryHolder;
import com.polito.mad.bikeponyapp.service.ServerCommunicationService;
import com.polito.mad.bikeponyapp.service.ServerCommunicationService.OnDestinationValidationListener;
import com.polito.mad.bikeponyapp.util.DialogCreator;
import com.polito.mad.bikeponyapp.util.ReroutingAsyncTask;
import com.polito.mad.bikeponyapp.util.ReroutingAsyncTask.OnReroutingListener;
import com.polito.mad.bikeponyapp.util.ResultMessage;
import com.polito.mad.bikeponyapp.util.SimpleAnimationListener;

public class DeliveryListViewFragment extends ListFragment implements
		LoaderCallbacks<Cursor>, ModelObserver, LocationObserver,
		ServiceStateChangeObserver, OnDestinationValidationListener,
		DragListener, DropListener, OnShowSingleLocationListener {

	private static final String DIRTY_LIST_PARAM_NAME = "dirtyList";
	private static final String IS_SAVING_PARAM_NAME = "isSaving";
	private static final String EDIT_MODE_ON_PARAM_NAME = "edit_mode_on";
	private static final String IS_ASKING_FOR_SAVE_PARAM_NAME = "isAskingForSave";
	private ListViewAdapter listViewAdapter;
	private OnShowSingleLocationListener onShowSingleLocationListener;
	private ServerCommunicationService service;
	private DragSortListView listView;
	private Dialog actualDialog;
	private ReroutingAsyncTask reroutingTask;
	private Location location;
	private LoaderManager loaderManager;
	private DeliveryListCursorLoader deliveryListCursorLoader;
	private DeliveriesRepository repository;
	private boolean editModeOn;
	private CompoundButton switchButton;
	private AlertDialog currentActiveDialog;
	private boolean isAskingForSave;
	private boolean isSaving;

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
	}

	@Override
	public void onActivityCreated(Bundle savedInstanceState) {
		super.onActivityCreated(savedInstanceState);
		/*
		 * We create a new list viewadapter passing the reposiroty from which to
		 * take the data
		 */
		if (listViewAdapter == null) {
			listViewAdapter = new ListViewAdapter(getActivity(), repository);
			listViewAdapter.setOnsShowSingleLocationListener(this);
		}
		
		listViewAdapter.setRedraw(false);
		if (savedInstanceState != null) {
			this.isAskingForSave = savedInstanceState.getBoolean(IS_ASKING_FOR_SAVE_PARAM_NAME);
			if(savedInstanceState.containsKey(EDIT_MODE_ON_PARAM_NAME))
				this.editModeOn = savedInstanceState.getBoolean(EDIT_MODE_ON_PARAM_NAME);
			this.isSaving= savedInstanceState.getBoolean(IS_SAVING_PARAM_NAME);
			if (savedInstanceState.getBoolean(DIRTY_LIST_PARAM_NAME))
				listViewAdapter.setDirtyObjects();
		}
		if(getListAdapter()==null)
			setListAdapter(listViewAdapter);

		/*restart the items loading process only if the repository is available */
		if (repository.getSessionCachedItems() == null|| repository.getSessionCachedItems().isEmpty())
			loaderManager.restartLoader(0, null, this);
	}

	@Override
	public void onStart() {
		super.onStart();
		initListView(editModeOn);
		switchButton = (CompoundButton) getView().findViewById(R.id.switch_to_edit);
		if(editModeOn && switchButton.isChecked())
			switchButton.setChecked(false);
		
		switchButton.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {

					@Override
					public void onCheckedChanged(CompoundButton buttonView,boolean isChecked) {

						if (isChecked ) {
							if(!editModeOn)
							{
								editModeOn=true;
								listViewAdapter.startEditState();
								listView.setDragEnabled(true);								
							}
							showToolbar();
						}
						else if(!isChecked ) {
							if(editModeOn)
							{
								if(listViewAdapter.isOrderChanged())
								{
									//switchButton.setChecked(true);
									askForSavingData();
									
								}else
								{
									editModeOn=false;
									listViewAdapter.endEditState(false);
									listView.setDragEnabled(false);
									hidesToolbar();
								}
							}
						}
					}
				});
		if(!switchButton.isChecked() && editModeOn)
		{
			editModeOn=false;
			switchButton.setChecked(true);
		}
		
		if(isAskingForSave)
		{
			showToolbar();
			askForSavingData();			
		}
		
		if(isSaving)
			showSaveNewTripDialog();
		
		/*switchButton.setChecked(editModeOn);*/
	}

	private void askForSavingData() {
		isAskingForSave=true;
		currentActiveDialog = DialogCreator.createConfirmAlertDialog(
				getActivity(), 
				getString(R.string.ask_for_saving_dialog_message),
				getString(R.string.ask_for_saving_dialog_title),
				new DialogInterface.OnClickListener() {
					
					@Override
					public void onClick(DialogInterface dialog, int which) {
						//switchButton.setChecked(true);
						editModeOn=true;
						isAskingForSave=false;
						switchButton.setChecked(true);
						saveOrderInList();
						
					}
				},
				new DialogInterface.OnClickListener() {
					
					@Override
					public void onClick(DialogInterface dialog, int which) {
						editModeOn=false;
						isAskingForSave=false;
						restoreOrderInList();
						dialog.dismiss();
						
					}
				});
		currentActiveDialog.show();
	}

	private void initListView(boolean enableDrag) {
		listView = (DragSortListView) getListView();
		listView.setDragEnabled(enableDrag);
		listView.setDragListener(this);
		listView.setDropListener(this);
		listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
	}

	/**
	 * The repository is set inside the deliveryListViewFragment
	 */
	@Override
	public void onAttach(Activity activity) {
		super.onAttach(activity);
		loaderManager = getLoaderManager();
		repository = ((RepositoryHolder) activity.getApplication()).getDeliveriesRepository();
		repository.registerObserver(this);
	}

	@Override
	public void onSaveInstanceState(Bundle outState) {
		if(switchButton!=null)
			outState.putBoolean(EDIT_MODE_ON_PARAM_NAME, switchButton.isChecked());
		outState.putBoolean(DIRTY_LIST_PARAM_NAME, isDirtyList());
		outState.putBoolean(IS_ASKING_FOR_SAVE_PARAM_NAME, isAskingForSave);
		outState.putBoolean(IS_SAVING_PARAM_NAME, isSaving);
		super.onSaveInstanceState(outState);
	}

	@Override
	public void onViewCreated(View view, Bundle savedInstanceState) {

		super.onViewCreated(view, savedInstanceState);
		/* configure item selection on the ListView */
		final ListView lv = (ListView) view.findViewById(android.R.id.list);
		lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {

			@Override
			public void onItemClick(AdapterView<?> adapterView,
					View clickedView, int position, long id) {
				clickedView.setSelected(true);
				showSingleItem(position);
			}
		});

		/* set up cancel and save buttons */
		ImageButton cancelButton = (ImageButton) getView().findViewById(R.id.cancelListSortButton);
		cancelButton.setOnClickListener(new View.OnClickListener() {

			@Override
			public void onClick(View v) {
				/* restore the list */
				restoreOrderInList();
			}
		});
		ImageButton confirmButton = (ImageButton) getView().findViewById(R.id.confirmListSortButton);
		confirmButton.setOnClickListener(new View.OnClickListener() {

			@Override
			public void onClick(View v) {
				/* send the validation request then stop the animation */
				saveOrderInList();
			}
		});
		Button refreshRoute = (Button) getView().findViewById(R.id.refreshRouteListSortButton);
		refreshRoute.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
				startRerouting(getActivity(), null);
			}
		});
	}

	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container,
			Bundle savedInstanceState) {
		return inflater.inflate(R.layout.delivery_list_layout, null);
	}

	@Override
	public Loader<Cursor> onCreateLoader(int id, Bundle args) {
		// Now create and return a CursorLoader that will take care of
		// creating a Cursor for the data being displayed.

		deliveryListCursorLoader = new DeliveryListCursorLoader(repository,getActivity());
		return deliveryListCursorLoader;
	}

	/**
	 * 
	 */
	@Override
	public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
		// Swap the new cursor in. (The framework will take care of closing the old cursor once we return.)

		ProgressBar pb = (ProgressBar) getView().findViewById(R.id.list_loading_progressbar);
		pb.setVisibility(View.GONE);
		
		if (data!=null && data.getCount() == 0) {
			
			TextView tv = (TextView) getView().findViewById(R.id.list_loading_message);
			tv.setText(getString(R.string.no_data_available));
			getView().findViewById(R.id.switch_to_edit_layout).setVisibility(View.INVISIBLE);
		}else
			
			getView().findViewById(R.id.switch_to_edit_layout).setVisibility(View.VISIBLE);
		
		listViewAdapter.updateData(data);
	}

	@Override
	public void onLoaderReset(Loader<Cursor> loader) {
		// This is called when the last Cursor provided to onLoadFinished()
		// above is about to be closed. We need to make sure we are no
		// longer using it.
		System.out.println("loader reset");
	}

	public void hidesToolbar() {
		final RelativeLayout toolbarLayout = (RelativeLayout) getView().findViewById(R.id.sortingButtonsLayout);
		if(toolbarLayout.getVisibility()==View.VISIBLE)
		{
			Animation bumpreverse = AnimationUtils.loadAnimation(getActivity(),R.anim.bump_out_reverse_view);
			toolbarLayout.setAnimation(bumpreverse);
			bumpreverse.setAnimationListener(new SimpleAnimationListener() {
				@Override
				public void onAnimationEnd(Animation animation) {
	
					toolbarLayout.setVisibility(View.GONE);
				}
			});
			bumpreverse.setFillAfter(true);
			bumpreverse.start();
		}
	}

	public void showToolbar() {
		final RelativeLayout toolbarLayout = (RelativeLayout) getView().findViewById(R.id.sortingButtonsLayout);
		
		if(toolbarLayout.getVisibility()!=View.VISIBLE)
		{
			if (listViewAdapter.isEmpty()) {
				toolbarLayout.findViewById(R.id.cancelListSortButton).setEnabled(false);
				toolbarLayout.findViewById(R.id.confirmListSortButton).setEnabled(false);
				toolbarLayout.findViewById(R.id.refreshRouteListSortButton).setEnabled(false);
	
			} else {
				toolbarLayout.findViewById(R.id.cancelListSortButton).setEnabled(true);
				toolbarLayout.findViewById(R.id.confirmListSortButton).setEnabled(true);
				toolbarLayout.findViewById(R.id.refreshRouteListSortButton).setEnabled(true);
			}
			Animation bump = AnimationUtils.loadAnimation(getActivity(),R.anim.bump_out_view);
			toolbarLayout.setAnimation(bump);
			bump.setAnimationListener(new SimpleAnimationListener() {
	
				@Override
				public void onAnimationStart(Animation animation) {
					super.onAnimationStart(animation);
					toolbarLayout.setVisibility(View.VISIBLE);
				}
	
				@Override
				public void onAnimationEnd(Animation animation) {
					getView().invalidate();
				}
			});
			bump.setFillBefore(true);// new !!! test
			bump.setFillAfter(true);
			bump.start();
		}
	}

	/**
	 * 
	 */
	@Override
	public void dataSetChanged() {
		if (isVisible())
			loaderManager.restartLoader(0, null, this);
	}

	/**
	 * 
	 */
	@Override
	public void updateLocation(Location location) {
		this.location = location;
	}

	/**
	 * 
	 * @return
	 */
	public List<DeliveryItem> getItems() {
		return listViewAdapter.getItems();
	}

	@Override
	public void serviceBound(ServerCommunicationService service) {
		System.out.println("service bound in delivery list view");
		this.service = service;
		this.service.setOnDestinationValidationListener(this);
		this.service.registerLocationObserver(this);

		if (listViewAdapter != null && (listViewAdapter.getItems() == null || listViewAdapter.getItems().isEmpty()))
			getLoaderManager().restartLoader(0, null, this);
		// listViewAdapter.notifyDataSetChanged();
	}

	@Override
	public void serviceDisconnected() {
		this.service = null;
		Toast.makeText(getActivity(),getString(R.string.service_disconnected),Toast.LENGTH_SHORT).show();
		/* some how destroy the adapter */
	}

	@Override
	public void onValidDestinations() {
		destinationResponseReceived();
		Toast.makeText(getActivity(), getString(R.string.trip_update_message),Toast.LENGTH_SHORT).show();
		for (DeliveryItem item : repository.getSessionCachedItems()) {
			item.setInvalidPosition(false);
		}
		listViewAdapter.validDeliveryReorder();
		switchButton.setChecked(false);
		getActivity().invalidateOptionsMenu();
		// hidesToolbar();
	}

	@Override
	public void onInvalidDestinations(ResultMessage resultMessage,String errorMessage) {
		destinationResponseReceived();
		Toast.makeText(getActivity(), getString(R.string.invalid_trip_error_message, errorMessage),Toast.LENGTH_LONG).show();
		if (resultMessage != null && resultMessage.getData() != null) {
			try {
				long invalidDeliveryId = resultMessage.getData().getLong("wrong_delivery_id");
				for (DeliveryItem item : repository.getSessionCachedItems()) {
					if (item.getDeliveryId() == invalidDeliveryId)
						item.setInvalidPosition(true);
				}
				listViewAdapter.notifyDataSetChanged();
			} catch (JSONException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

		}
	}

	private void destinationResponseReceived() {
		isSaving=false;
		if (actualDialog != null && actualDialog.isShowing())
			actualDialog.dismiss();
	}

	/**
	 * Recompute the trip between the cities by invoking the google directions
	 * service
	 */
	@SuppressWarnings("unchecked")
	public void startRerouting(final Context context,final OnReroutingListener externalReroutingListener) {
		/* start the new rerouting process */
		if (location == null) {
			Toast.makeText(
					getActivity(),
					getString(R.string.trip_update_location_error),
					Toast.LENGTH_LONG).show();
			return;
		}
		if (reroutingTask != null && reroutingTask.getStatus()==AsyncTask.Status.RUNNING)
			reroutingTask.cancel(true);

		reroutingTask = new ReroutingAsyncTask(getActivity(), location) {

			@Override
			protected void onReroutingCompleted(boolean success,String message, List<DeliveryItem> deliveryItems) {
				super.onReroutingCompleted(success, message, deliveryItems);
				String toastMessage = "";
				if (success) {
					repository.setSessionCachedItems(deliveryItems);	
					if(!listViewAdapter.isOrderChanged())
					{
						repository.updateAll(deliveryItems);
					}
					listViewAdapter.resetDirtyRouting();
					listViewAdapter.setRedraw(true);
					toastMessage = getString(R.string.rerouting_succesfull);
				} else {
					toastMessage = getString(R.string.rerouting_failed, message);
				}
				if (externalReroutingListener != null)
					externalReroutingListener.onReroutingCompleted(success,
							message, deliveryItems);
				/*
				 * show a message asking the user whether he wants to accept the
				 * new routing or not
				 */
				Toast.makeText(context, toastMessage, Toast.LENGTH_SHORT).show();
			}
		};

		reroutingTask.execute(listViewAdapter.getItems());
	}

	@Override
	public void drop(int from, int to) {
		if(listViewAdapter.checkDrop(from, to))
		{
			listViewAdapter.notifyDataSetChanged();
		}else
		{
			listView.cancelDrag();
			Toast.makeText(getActivity(), getString(R.string.item_reorder_not_allowed),Toast.LENGTH_LONG).show();
		}
	}

	@Override
	public void drag(int from, int to) {
		/*
		if (!listViewAdapter.swap(from, to)) {
			listView.cancelDrag();
			Toast.makeText(getActivity(), "Operation not allowed",Toast.LENGTH_SHORT).show();
		}*/
	}

	public void showDialog(String message, String title) {
		DialogCreator.createAlertDialog(getActivity(), message, title).show();
	}

	@Override
	public void showSingleItem(int itemIndex) {
		if(actualDialog!=null && actualDialog.isShowing())
			actualDialog.dismiss();
		if (onShowSingleLocationListener != null)
			onShowSingleLocationListener.showSingleItem(itemIndex);
	}

	public OnShowSingleLocationListener getOnShowSingleLocationListener() {
		return onShowSingleLocationListener;
	}

	public void setOnShowSingleLocationListener(
			OnShowSingleLocationListener onShowSingleLocationListener) {
		this.onShowSingleLocationListener = onShowSingleLocationListener;
	}

	public boolean isDirtyList() {
		return listViewAdapter != null && listViewAdapter.isDirtyRouting();
	}

	@Override
	public void releaseRepositoryConnection() {
		loaderManager.destroyLoader(0);
	}

	public boolean isPathChanged() {
		return listViewAdapter == null || listViewAdapter.isRedraw();
	}

	public void startListEditMode() {
		if (switchButton != null && isVisible())
			switchButton.setChecked(true);
		else
			editModeOn = true;
	}

	public void stopListEditMode() {
		if (switchButton != null)
			switchButton.setChecked(false);
		else
			editModeOn = false;
	}

	public void stopRerouting() {
		if(reroutingTask!=null && reroutingTask.getStatus()!= Status.FINISHED)
		{
			reroutingTask.cancelReroutingTask();
		}
	}

	private void restoreOrderInList() { 
		editModeOn=false;
		switchButton.setChecked(false);
		hidesToolbar();
		listViewAdapter.endEditState(true);
		getLoaderManager().restartLoader(0, null,DeliveryListViewFragment.this);
	}

	private void saveOrderInList() {
		
		if (listViewAdapter.isOrderChanged()) {
			showSaveNewTripDialog();
			service.destinationsChanged(listViewAdapter.getItems());
			// activity.destinationsChanged(listViewAdapter.g)
		} else {
			listViewAdapter.notifyDataSetChanged();
			Toast.makeText(getActivity(),getString(R.string.trip_already_checked),Toast.LENGTH_SHORT).show();
		}
	}

	private void showSaveNewTripDialog() {
		actualDialog = DialogCreator.createAlertDialog(getActivity(), getString(R.string.trip_validation_dialog_message),getString(R.string.trip_validation_dialog_title));
		actualDialog.setCancelable(false);
		actualDialog.setCanceledOnTouchOutside(false);
		actualDialog.show();
		isSaving=true;
	}
	
	@Override
	public void onStop() {
		super.onStop();
		if(currentActiveDialog!=null && currentActiveDialog.isShowing())
			currentActiveDialog.dismiss();
	}
}
